iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 14
0

重點

Andy:

  1. ES5 語言規範的 section 9 定義了幾種“抽像運算”(僅供內部使用的操作)。我們將特別關注於:ToString、ToNumber、和ToBoolean
  2. document.all:一個由DOM(不是JS引擎本身)給你的JS程序提供的類數組(對象)。它曾經像一個普通對像那樣動作——是一個truthy。但不再是了 QQ。

Jason:

  1. 重點就是一個,清楚的知道變數的型別,避免讓js自動幫你轉型,不然會脫離掌握,剩下的都是對工具的熟練度、熟悉度

Tony:

  1. 背出 falsy,或寫下 falsy。
  2. 書上列的明確轉型,要能夠知道哪些算公認的明確轉型。+ ~ | !! ?:
  3. 要使用這些明確轉型,確保你的夥伴都懂這些。

Lai:

  1. 如果你試圖字串化一個可能含有非法 JSON 值的物件,或是在那個 object 上放了不適合序列化的值,應該爲此定義一個 toJSON() 方法,回傳該物件的一個 JSON-safe 版本。
  2. 日期轉爲數字可以用強制的方式取得。但取得時間有它自己的語法,應該儘量使用它本來的方式取得。
  3. 可以利用 ! 強制轉換,但因爲它會反轉,所以開發人員會利用兩個!!

Kai:

  1. ECMAScript 定義了 abstract operatoin
  2. 轉型要知道轉型的前後的東西是否是你要的

Chris:

  1. JSON.stringify(a, b, c)
  2. -1 是有意義的
  3. ToNumber 會先 ToPrimitive

Henry

  1. 符哨值 -1 是有原因的(~)
  2. true 會被轉成 1,false, null 會是 0,undefined 是 NaN
  3. JSON.stringify 可以傳入三個參數

mango:

  1. toString(),toNumber(),toBoolean()是如何做值轉換
  2. toBoolean()只有五個會回傳false,其他都是true

turtle:

  1. 明確與隱含強制轉型對每個人來說定義都不一定一樣,還是需要團隊溝通。
  2. 並非所有 物件 都是 truthy

Jimmy:

  1. JSON.stringify()與toJSON()2者間的差異
  2. ~~ 逐次元雙次反轉動作非常類似!!的真假值雙次否定
  3. 不要對parseInt()傳入非字串的元素,不然會出現奇怪的行為

共筆區域

強制轉型從以前到現在都具有爭議,有些人認為他是 JS 的缺陷,但對於一些怪異的部分,我們應該徹頭徹尾去了解它的優點與缺點。

轉換值

【 強制轉型 】
定義:將一個值從某個型別轉換成另一個型別的動作,如果明確地進行,通常就叫做型別轉換,而如果隱含地進行,就稱為強制轉型。
特徵:在 JavaScript 中強制轉型的結果,永遠都會是純量的基本型別值,像是 String、Number 或 Boolean,而不是 Object、Function 之類的複雜的值。

有關的專有名詞

  • type casting(type conversion):發生於靜態型別語言的編譯時期。
  • type coercion:動態語言在執行時期所進行的轉換。

以上多數人通稱為「coercion」。所以區分的方式分為兩種:
明確的強制轉型(explicit coercion):可明顯看出某個型別轉換動作。
隱含的強制轉型(implicit coercion):型別轉換動作是某個刻意進行其他作業所產生的較不明顯的副作用。

var a = 42;
var b = a + "" 
// 感覺程式的目的是要做變數與字串的相加合併(或相加?),但他自己另外進行的轉型。
var c = String(a) 
// 目的只有一個:就是要轉型。

抽象的值運算

在探討「明確」和「隱含」前,必須先瞭解值如何轉型為某個基本型別值。
ES5 規格的 section 9 定義了幾個抽象運算,並附有值轉換的規則。

現在主要了解 ToString、ToNumber 及 ToBoolean 身上,也可以看一下 ToPrimitive。

ToString

任何非 string 的值被強制轉型為 string 表示值,轉換過程是由 section 9.8 中的 ToString 抽象運算來處理。

ToSting —— ECMAScript® Language Specification

  • 字串強制轉型的結果
別值 轉字串結果
null "null"
undefined "undefined"
true "true"
123(一般數字型別) "123"
  • 特殊案例——特大指數
var a = 1.07 * 1000 * 1000 * 1000 ..
// 1.07 乘以 1000 七次(21個零)
a.toString();
//"1.07e+21"

物件

對物件來說,使用 Object.prototype.toString(),會回傳內部的 [[Class]],如果物件有他自己的 toString 方法,而你又把這個物件當作 string 來用,那他的 toString 方法就會被自動呼叫。

注意:如果一個物件被強制轉型成一個字串,那過程會經過 ToPrimitive 的抽象處理,詳見ECMA 5 section9.1
ToNumber 後面會講,現在先跳過。
補充說明

陣列


陣列轉字串,會先將陣列的值字串化後,再以,連接各個值。

toString 可以被明確地被呼叫,或是當一個不是字串的值被用在字串的情境中,他就會被自動呼叫。

Array 本身有 toString 依照 prototype toString 的方法會覆蓋物件的toString

JSON 字串化

使用 JSON.stringify(..),他會將一個值序列化為一個 JSON 相容的字串。這種序列化和強制轉型不完全相同,但它和 ToString 規則有關連,所以稍微說一下。

JSON.stringify( 42 );	// "42"
JSON.stringify( "42" );	// ""42"" (一个包含雙引號的字串)
JSON.stringify( null );	// "null"
JSON.stringify( true );	// "true"

任何 JSON-safe 的值都可以被 JSON.stringify(..)

JSON-safe

  • 定義:可被有效表達為 JSON 表示值的任何值。
  • 不是的範例:
    • undefined
    • function
    • symbol
    • 帶有循環參考的物件。(拋出錯誤)
    • 在陣列中碰到,會被取代為 null。
    • 在物件的 key 值遇到,該名值對會被忽略。
JSON.stringify( undefined );					
// undefined
JSON.stringify( function(){} );					
// undefined

JSON.stringify( [1,undefined,function(){},4] );	
// "[1,null,null,4]"
JSON.stringify( { a:2, b:function(){} } );		
// "{"a":2}"

JSON 字串化的特殊行為:一個物件值定義有 toJSON(),那此方法就會被先呼叫,以取得一個用於序列化的物件。
如果想字串化一個非 JSON-safe 的值,那可以定義一個 toJSON() ,回傳一個該物件 JSON-safe 的版本。

var o = { };

var a = {
	b: 42,
	c: o,
	d: function(){}
};

// 在 a 內部建立一個循環參考
o.e = a;

// 會對 JSON.stringify( a ); 丟出錯誤

// 定義一個 JSON 值用於序列化
a.toJSON = function() {
	// 序列化過程只含有 b 特性
	return { b: this.b };
};

JSON.stringify( a ); // "{"b":42}"

常見的誤解

toJSON 不是轉為 JSON 字串,而是轉為一個適合用於字串化的 JSON-safe 值。

  • 正確觀念範例:

    var a = {
        val: [1,2,3],
    
        toJSON: function(){
            return this.val.slice( 1 );
        }
    };
    
    JSON.stringify( a ); // "[2,3]"
    
  • 常見誤解範例:

    
    var b = {
        val: [1,2,3],
    
        toJSON: function(){
            return "[" +
                this.val.slice( 1 ).join() +
            "]";
        }
        // 字串化所回傳的字串,而非陣列本身。
    };
    
    JSON.stringify( b ); // ""[2,3]""
    
  • 怪異的部分

如果有人知道可以告訴我,感謝。

JSON.stringify 的引數

JSON.stringify(...)的第二個引數:
  • 名稱:replacer
  • 引數型別:可以是陣列、function
  • 作用:自訂一個物件的遞迴序列化。
  • 用途:提供另一種過濾非 JSON-safe 值的方式
  • 引數是陣列:應該為字串組所構成,每個字串指定的都是物件內的一個特性名稱。
  • 引數是函式:這個函式中會有特性和值兩個引數,他會被呼叫一次,而物件中的名值對都會被依序傳入,在序列化過程中如果跳過一個特性,就會回傳undefined,否則就回傳所提供的值。
  • 範例:
var a = {
	b: 42,
	c: "42",
	d: [1,2,3]
};

JSON.stringify( a, ["b","c"] ); // "{"b":42,"c":"42"}"

JSON.stringify( a, function(k,v){
	if (k !== "c") return v;
} );
// "{"b":42,"d":[1,2,3]}"



JSON.stringify(...)的第三個引數:
  • 名稱:space
  • 引數型別:正整數或字串
  • 作用:space 可以指定每個縮排層級要用幾個空字元的一個正整數。
  • 用途:用於縮排以產生美觀的輸出,易讀性提高。
  • 範例:
var a = {
	b: 42,
	c: "42",
	d: [1,2,3]
};

JSON.stringify( a, null, 3 );
// "{
//    "b": 42,
//    "c": "42",
//    "d": [
//       1,
//       2,
//       3
//    ]
// }"

JSON.stringify( a, null, "-----" );
// "{
// -----"b": 42,
// -----"c": "42",
// -----"d": [
// ----------1,
// ----------2,
// ----------3
// -----]
// }"

小結語

  1. 字串、布林、數字以及 null 這些值字串化成 JSON 的方式,基本上就和他們轉為字串型別的方式相同。
  2. 物件值傳進 JSON.stringify,而那個物件值上有一個 toJSON 方法,這個 toJSON 在字串化前會被自動呼叫,而這個物件可以算是隱含的強制轉型為 JSON-safe。(因為我們沒有主動呼叫 toJSON)

ToNumber

任何不是數字型別而被當作數字來用,例如數學運算,ES5 有定義了 ToNumber 的抽象運算。

轉型前 轉型後
true 1
false 0
undefined NaN
null 0

字串轉數字

對字串來說,ToNumber 跟數值字面值(第六章)的規則和語法很像。
如果非數字字面值轉型失敗,結果就是 NaN,差別在於 0 前綴的八進位數字不會被當作八進位,而是十進位。

(八進位是 ES5 之前的事)

物件(陣列)

它會先被轉為基本型別值等效值,而產生結果,而這個結果值會根據剛提到的規則被強制轉換成數字。

為了轉換基本型別的等效值,ToPrimitive 抽象運算:

  1. 看有沒有 valueOf() 方法,如果有就回傳一個基本型別值,那個值就會被用於強制轉型,看有沒有基本型別值。
  2. 如果沒有,toString() 就會提供用於強制轉型的值,看有沒有基本型別值。
  3. 如果兩者都沒辦法提供基本型別值,就會丟出 TypeError。

範例:

var a = {
	valueOf: function(){
		return "42";
	}
};

var b = {
	toString: function(){
		return "42";
	}
};

var c = [4,2];
c.toString = function(){
	return this.join( "" );	// "42"
};

Number( a );			// 42
Number( b );			// 42
Number( c );			// 42
Number( "" );			// 0
Number( [] );			// 0
Number( [ "abc" ] );	// NaN

上一篇
Day13:圖解 HTTP Chapter06 HTTP 首部 筆記精要(下)
下一篇
Day15:YDKJS 第四次讀書會 筆記(下)
系列文
寇丁人妻的前端書蟲日誌30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言